/**
 * Copyright � 2012 GestureSoft. ALL RIGHTS RESERVED.
 * This file contains proprietary and GestureSoft CONFIDENTIAL Information.
 * Use, disclosure or reproduction is prohibited.
 * 
 * @fileName GPSDetecter.as
 * @creationDate Aug 8, 2012
 * @author 
 * @version 1.0
 */
package com.gesturesoft.imultimax.model
{
	
	import com.gesturesoft.imultimax.events.GPSEvent;
	import com.gesturesoft.imultimax.model.error.DataBaseError;
	import com.gesturesoft.imultimax.model.interfaces.IDatabaseManager;
	import com.gesturesoft.imultimax.model.utility.Application;
	import com.gesturesoft.imultimax.model.utility.MathUtil;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.GeolocationEvent;
	import flash.events.TimerEvent;
	import flash.sensors.Geolocation;
	import flash.utils.Timer;
	
	import mx.collections.ArrayCollection;

	public class GPSDetecter extends EventDispatcher
	{
		
		public static const GPS_ENABLED:String = "GPS Enabled";
		public static const GPS_REQUIRED:String = "Please turn on your GPS before starting";
		public static const GPS_UNAVAILABLE:String = "GPS not supported";
		
		private const RADIUS_OF_EARTH_IN_MILES:int = 3963;
		private const RADIUS_OF_EARTH_IN_FEET:int =20925525;
		private const RADIUS_OF_EARTH_IN_KM:int =6378;
		private const RADIUS_OF_EARTH_IN_M:int =6378000;
		private var _appFirstTime:Boolean = false;
		
		[Bindable]private var geoLocation:Geolocation;
		[Bindable]private var latitude:String;
		[Bindable]private var longitude:String;
		
		[Bindable]public var isFirstTime:Boolean;
		
		public function GPSDetecter()
		{
			
		}
		
		public function initiateGPS():String
		{
			
			var returnMsg:String;
			if(Geolocation.isSupported){
				geoLocation = new Geolocation();
				if(geoLocation.muted)
				{
					
					//dispatchEvent(new GPSEvent(GPSEvent.GPS_INITIATE_STATUS,GPS_REQUIRED));
					returnMsg = GPS_REQUIRED;
					
				}
				else
				{
					
					geoLocation.setRequestedUpdateInterval(3000);
					_appFirstTime = Application.isFirstTime();
					geoLocation.addEventListener(GeolocationEvent.UPDATE, handleLocationRequest); 
					returnMsg = GPS_ENABLED;
					
				}
				
			}
			else 
			{
				
				//dispatchEvent(new GPSEvent(GPSEvent.GPS_INITIATE_STATUS,GPS_UNAVAILABLE));
				returnMsg = GPS_UNAVAILABLE;
				
			}
			
			return returnMsg;
			
		}
		
		private function handleLocationRequest(event:GeolocationEvent):void
		{
			
			latitude = MathUtil.RoundDecimal(event.latitude,2).toString();
			longitude = MathUtil.RoundDecimal(event.longitude,2).toString();
			
			geoLocation.removeEventListener(GeolocationEvent.UPDATE, handleLocationRequest);
			
			//updateGPSInfo();
			
			/*if(isFirstTime)
			{
				
				insertGPSInfo();
				
			}
			else
			{
				
				updateGPSInfo();
				
			}*/
			
			//dispatchEvent(new GPSEvent(GPSEvent.GPS_INITIATE_STATUS,GPS_ENABLED));
			
		}
		
		private function updateGPSInfo():void
		{
			
			var arrayCol:ArrayCollection = new ArrayCollection();
			var gesture_application:Application = new Application();
			
			var minPoint:Array = subtractDistance(Number(latitude), Number(longitude));
			var maxPoint:Array = addDistance(Number(latitude), Number(longitude));
			
			
			var query:String = "SELECT * FROM IM_GPS_LOCATION WHERE min_lattitude<='"+Number(latitude)+"' AND max_lattitude>='"+Number(latitude)+"'"+
				" AND min_longitude<='"+Number(longitude)+"' AND max_longitude>='"+Number(longitude)+"'";
			
			try
			{
				
				var dbMangager:IDatabaseManager = new DataBaseManager();
				arrayCol = dbMangager.getData(query);
				
			} 
			catch(error:DataBaseError) 
			{
				
				throw error;
				trace("error in GPSDetector.updateGPSInfo");
				
			}
			
			if(arrayCol.length == 0)
			{
				
				insertGPSInfo(minPoint[0], minPoint[1],maxPoint[0], maxPoint[1]);
				if( !_appFirstTime )
					dispatchEvent(new GPSEvent(GPSEvent.GPS_LOCATION_CHANGED));
				
			}
			
		}
		
		private function insertGPSInfo(minLattitude:Number, maxLattitude:Number,
									   minLongitude:Number, maxLongitude:Number):void
		{
			
			var dbMangager:IDatabaseManager = new DataBaseManager();
			var query:String = "INSERT INTO IM_GPS_LOCATION(min_lattitude, max_lattitude, min_longitude, max_longitude ) VALUES(" +
				minLattitude+","+maxLattitude+","+minLongitude+","+maxLongitude+")";
			
			try
			{
				
				dbMangager.execute( query );
				
			} 
			catch(error:DataBaseError) 
			{
				
				throw error;
				trace("error in GPSDetector.insertGPSInfo");
				
			}
			
			
			/*var gesture_application:Application = new Application();
			gesture_application.insertLocation(longitude,latitude,"0");*/
			
		}
		
		private function distanceBetweenCoordinates(lat1:Number,lon1:Number,lat2:Number,lon2:Number,units:String="km"):Number
		{
			
			var R:int = RADIUS_OF_EARTH_IN_KM;
			if (units == "km")
			{
				
				R = RADIUS_OF_EARTH_IN_KM;
				
			}
			
			if (units == "meters")
			{
				
				R = RADIUS_OF_EARTH_IN_M;
				
			}
			
			if (units =="feet")
			{
				
				R= RADIUS_OF_EARTH_IN_FEET;
				
			}
			
			var dLat:Number = (lat2-lat1) * Math.PI/180;
			var dLon:Number = (lon2-lon1) * Math.PI/180;
			
			var lat1inRadians:Number = lat1 * Math.PI/180;
			var lat2inRadians:Number = lat2 * Math.PI/180;
			
			var a:Number = Math.sin(dLat/2) * Math.sin(dLat/2) + 
				Math.sin(dLon/2) * Math.sin(dLon/2) * 
				Math.cos(lat1inRadians) * Math.cos(lat2inRadians);
			var c:Number = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
			var d:Number = R * c;
			
			return d;
			
		}
		
		/**
		 * This is the function for add distance to a geographical point
		 * 
		 * @functionName addDistance
		 * @creationDate Aug 27, 2012
		 * @author micmac
		 * @params lat:int, long:int
		 */
		private function addDistance(lat1:Number, lon1:Number):Array
		{
			
			var d:Number = 20;   //Distance travelled
			var R:Number = 6371;
			var brng:Number = 0;
			var LatMax:Number;
			brng = toRad(brng); 
			var lat1:Number = toRad(lat1)
			var lon1:Number = toRad(lon1);
			var lat2:Number = Math.asin( Math.sin(lat1)*Math.cos(d/R) + 
				Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng) );
			
			var lon2:Number = lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1), 
				Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));
			lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI;  
			lat2= toDeg(lat2);
			lon2= toDeg(lon2);
			
			return [lat2, lon2];
		}
		
		/**
		 * This is the function for subtract distance to a geographical point
		 * 
		 * @functionName addDistance
		 * @creationDate Aug 27, 2012
		 * @author micmac
		 * @params lat:int, long:int
		 */
		private function subtractDistance(lat1:Number, lon1:Number):Array
		{
			
			var d:Number = 20;   //Distance travelled
			var R:Number = 6371;
			var brng:Number = 0;
			var LatMax:Number;
			brng = toRad(brng); 
			var lat1:Number = toRad(lat1)
			var lon1:Number = toRad(lon1);
			var lat2:Number = Math.asin( Math.sin(lat1)*Math.cos(d/R) - 
				Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng) );
			
			var lon2:Number = lon1 - Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1), 
				Math.cos(d/R)+Math.sin(lat1)*Math.sin(lat2));
			lon2 = (lon2+3*Math.PI) % (2*Math.PI) + Math.PI;  
			lat2= toDeg(lat2);
			lon2= toDeg(lon2);
			
			return [lat2, lon2];
			
		}
		
		private function toRad(Value:Number):Number
		{
			/** Converts numeric degrees to radians */
			return Value * Math.PI / 180;
			
		}
		
		private function toDeg(Value:Number):Number
		{
			return Value * 180 / Math.PI;
		}
		
		private function retrieveHandler(val:String):ArrayCollection
		{
			
			var result:ArrayCollection;
			
			var _iDBManager:IDatabaseManager = new DataBaseManager();
			_iDBManager.prepareTable("locationtable");
			result = _iDBManager.getData(val);
			
			return result;
			
		}
		
	}
	
}